/*-------------------------------------------------------------------------
 *
 * conversioncmds.c
 *	  转换创建命令支持代码
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/conversioncmds.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/conversioncmds.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"

/*
 * 创建转换
 */
ObjectAddress CreateConversionCommand(CreateConversionStmt *fc_stmt)
{
	Oid			fc_namespaceId;
	char	   *fc_conversion_name;
	AclResult	fc_aclresult;
	int			fc_from_encoding;
	int			fc_to_encoding;
	Oid			fc_funcoid;
	const char *fc_from_encoding_name = fc_stmt->for_encoding_name;
	const char *fc_to_encoding_name = fc_stmt->to_encoding_name;
	List	   *fc_func_name = fc_stmt->func_name;
	static const Oid fc_funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID, BOOLOID};
	char		fc_result[1];
	Datum		fc_funcresult;

	/* 将名称列表转换为一个名称和命名空间 */
	fc_namespaceId = QualifiedNameGetCreationNamespace(fc_stmt->conversion_name,
													&fc_conversion_name);

	/* 检查我们在目标命名空间中是否有创建权限 */
	fc_aclresult = pg_namespace_aclcheck(fc_namespaceId, GetUserId(), ACL_CREATE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_SCHEMA,
					   get_namespace_name(fc_namespaceId));

	/* 检查编码名称 */
	fc_from_encoding = pg_char_to_encoding(fc_from_encoding_name);
	if (fc_from_encoding < 0)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("source encoding \"%s\" does not exist",
						fc_from_encoding_name)));

	fc_to_encoding = pg_char_to_encoding(fc_to_encoding_name);
	if (fc_to_encoding < 0)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("destination encoding \"%s\" does not exist",
						fc_to_encoding_name)));

	/*
	 * 我们认为从或到 SQL_ASCII 的转换毫无意义。 （如果
	 * 您希望更改此内容，请注意 pg_do_encoding_conversion() 及其
	 * 姐妹函数对源或目标编码为 SQL_ASCII 的任何转换都有硬编码的快速通道，
	 * 因此声明用于这种情况的编码转换函数将永远不会被使用。）
	 */
	if (fc_from_encoding == PG_SQL_ASCII || fc_to_encoding == PG_SQL_ASCII)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("encoding conversion to or from \"SQL_ASCII\" is not supported")));

	/*
	 * 检查转换函数的存在性。 函数名称可以是合格名称。
	 */
	fc_funcoid = LookupFuncName(fc_func_name, sizeof(fc_funcargs) / sizeof(Oid),
							 fc_funcargs, false);

	/* 检查它是否返回 int4，否则可能是错误的函数 */
	if (get_func_rettype(fc_funcoid) != INT4OID)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("encoding conversion function %s must return type %s",
						NameListToString(fc_func_name), "integer")));

	/* 检查我们是否对该函数有 EXECUTE 权限 */
	fc_aclresult = pg_proc_aclcheck(fc_funcoid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION,
					   NameListToString(fc_func_name));

	/*
	 * 检查转换函数是否适合请求的源和目标编码。 我们通过
	 * 使用空字符串调用该函数来做到这一点； 如果转换函数无法
	 * 执行请求的转换，则应该抛出错误。
	 */
	fc_funcresult = OidFunctionCall6(fc_funcoid,
								  Int32GetDatum(fc_from_encoding),
								  Int32GetDatum(fc_to_encoding),
								  CStringGetDatum(""),
								  CStringGetDatum(fc_result),
								  Int32GetDatum(0),
								  BoolGetDatum(false));

	/*
	 * 对于空输入，函数应该返回 0。 也可以检查一下。
	 */
	if (DatumGetInt32(fc_funcresult) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("encoding conversion function %s returned incorrect result for empty input",
						NameListToString(fc_func_name))));

	/*
	 * 一切正常，继续（可能的失败将是重复的转换
	 * 名称）
	 */
	return ConversionCreate(fc_conversion_name, fc_namespaceId, GetUserId(),
							fc_from_encoding, fc_to_encoding, fc_funcoid, fc_stmt->def);
}
